
介面是一種用於定義型別結構的抽象概念。它們提供了一種方法來描述物件的形狀,指定它們應該具有哪些屬性和方法。換句話說,介面定義了一個物件應該是什麼樣子,但不關心具體的實現細節。
在以下的例子中,我們使用 interface 關鍵字來定義了一個介面叫做 Person。然後,我們宣告了一個名為 person 的變數,並明確指定了它的型別為 Person。這樣做的目的是約束 person 物件的形狀必須與 Person 介面中所定義的一致。
interface Person {
name: string;
age: number;
greet: (phrase: string) => void;
}
const person: Person = {
name: '肉鬆',
age: 30,
greet: (phrase: string) => {
console.log(`我是${person.name} - ${phrase}`); // 我是肉鬆 - 一起學習 TypeScript
},
};
person.greet('一起學習 TypeScript');
當定義的變數比介面少了一些屬性(少了 age 屬性):
interface Person {
name: string;
age: number;
greet: (phrase: string) => void;
}
const person: Person = { // TypeScript 報錯
name: '肉鬆',
greet: (phrase: string) => {
console.log(`我是${person.name} - ${phrase}`); // 我是肉鬆 - 一起學習 TypeScript
},
};
person.greet('一起學習 TypeScript');

當定義的變數比介面多了一些屬性(多了 gender 屬性):
interface Person {
name: string;
age: number;
greet: (phrase: string) => void;
}
const person: Person = {
name: '肉鬆',
age: 30,
gender: 'male', // TypeScript 報錯
greet: (phrase: string) => {
console.log(`我是${person.name} - ${phrase}`); // 我是肉鬆 - 一起學習 TypeScript
},
};
person.greet('一起學習 TypeScript');

當定義的介面中屬性加上 readonly 表示該屬性被建立後不可被修改(userId 屬性):
interface Person {
readonly userId: number;
name: string;
age: number;
}
const person: Person = {
userId: 123,
name: '肉鬆',
age: 30,
};
person.userId = 456; // TypeScript 報錯

當定義的介面中屬性加上 ? 表示該屬性可以存在,也可以不存在。
age 屬性不存在:
interface Person {
name: string;
age?: number;
}
const person: Person = {
name: '肉鬆',
};
age 屬性存在:
interface Person {
name: string;
age?: number;
}
const person: Person = {
name: '肉鬆',
age: 30,
};
這時仍然不允許新增未定義的屬性(多了 gender 屬性):
const person: Person = {
name: '肉鬆',
age: 30,
gender: 'male', // TypeScript 報錯
};
允許我們定義一個介面,其中可以包含不固定名稱的屬性,並指定它的型別。
當索引簽名型別為 any:
表示我們可以在物件中包含任何屬性,這些屬性的值可以是任何型別,包括數字、字串、布林、物件、函式等。
interface ErrorContainer1 {
id: number;
[key: string]: any;
}
const errorBag1: ErrorContainer1 = {
id: 123,
email: '無效的信箱格式',
isActive: true,
};
當索引簽名型別為 string:
表示我們可以在物件包含任何屬性,但這些屬性的值必須都是字串型別。
特別注意:一旦定義任意屬性的型別,確定屬性與可選屬性的型別都必須是該任意屬性型別的子集。
interface ErrorContainer2 {
id: number; // TypeScript 報錯,類型 'number' 的屬性 'id' 不可指派給 'string' 索引類型 'string'
[key: string]: string;
}
const errorBag2: ErrorContainer2 = {
id: '123',
email: '無效的信箱格式',
age: 30, // TypeScript 報錯,類型 'number' 不可指派給類型 'string'
};
讓我們修改程式碼以解決 TypeScript 錯誤:
interface ErrorContainer2 {
id: string;
[key: string]: string;
}
const errorBag2: ErrorContainer2 = {
id: '0123',
email: '無效的信箱格式',
age: '30',
};
我們可以使用 extends 關鍵字將一個介面繼承到另一個介面,從而擴展介面的屬性。
interface Person {
name: string;
age: number;
}
interface User extends Person {
employeeId: string;
}
const user: User = {
name: '肉鬆',
age: 30,
employeeId: '123',
};
在上方的程式碼中,user 物件被定義為符合 User 介面,而 User 介面則繼承了 Person 介面。這意味著 user 物件必須包含 User 介面中的所有屬性,以及由 Person 介面繼承的 name 和 age 這兩個屬性。
type 與 interface 合併type 關鍵字建立一個名為 Gender 的型別別名。這個型別別名被設定為只能取 male 或 female 的字串值。Person 介面中,gender 屬性的型別被設定為我們剛剛定義的型別別名 Gender。person 物件,符合 Person 介面,此時,如果嘗試將 gender 屬性的值設定為除了 male 或 female 之外的字串值,TypeScript 將會產生錯誤。type Gender = 'male' | 'female';
interface Person {
name: string;
age: number;
gender: Gender;
}
const person: Person = {
name: '肉鬆',
age: 30,
gender: 'male', // 若嘗試賦予 male 或 female 外的字串值,TypeScript 會報錯
};
type 與 interface 差異談到 TypeScript 中的型別定義時,主要有兩個選擇:type 和 interface。以下是它們之間的主要差異,以便更清楚地理解它們:
type 來定義型別。& 和 | 運算符來組合多個型別。typeof 獲取變數的型別。interface 來定義介面。typeof 用於變數,而僅用於類別、函數和命名空間。readonly 表示該屬性被建立後不可被修改。? 表示該屬性可以存在,也可以不存在。any 時,屬性的值可以是任何型別。反之,如果定義了任意屬性型別,其他屬性和可選屬性的型別必須是該任意屬性型別的子集。extends 關鍵字可以擴展一個介面的屬性。